﻿#include "videoPlayer.h"

videoPlayer::videoPlayer(QObject* parent) :
    QThread(parent) ,
    audioDecodeThread(this),
    videoDecodeThread(this)
{
    qRegisterMetaType<QImage>("QImage");

    this->connectSignalAndSlotToParent();
    this->connectSignalAndSlotToAudioVideoThread();
}
videoPlayer::~videoPlayer()
{
    this->disconnectSignalAndSlotToParent();
    this->disconnectSignalAndSlotToAudioVideoThread();
}

void videoPlayer::connectSignalAndSlotToParent()
{

    connect(this,
            SIGNAL(startVideoPlaySignal(QString)),
            this,
            SLOT(startVideoPlaySlot(QString)));

    connect(this,
            SIGNAL(pauseVideoPlaySignal(bool)),
            this,
            SLOT(pauseVideoPlaySlot(bool)));

    connect(this,
            SIGNAL(stopVideoPlaySignal()),
            this,
            SLOT(stopVideoPlaySlot()));
    connect(this,
            SIGNAL(seekVideoPlaySignal(int)),
            this,
            SLOT(seekVideoPlaySlot(int)));


}

void videoPlayer::disconnectSignalAndSlotToParent()
{
    disconnect(this,
               SIGNAL(startVideoPlaySignal(QString)),
               this,
               SLOT(startVideoPlaySlot(QString)));

    disconnect(this,
               SIGNAL(pauseVideoPlaySignal(bool)),
               this,
               SLOT(pauseVideoPlaySlot(bool)));

    disconnect(this,
               SIGNAL(stopVideoPlaySignal()),
               this,
               SLOT(stopVideoPlaySlot()));

    disconnect(this,
            SIGNAL(seekVideoPlaySignal(int)),
            this,
            SLOT(seekVideoPlaySlot(int)));
}

void videoPlayer::connectSignalAndSlotToAudioVideoThread()
{
    connect(&(this->videoDecodeThread),
            SIGNAL(updatePlayImgToPlayerSignal(QImage)),
            this,
            SLOT(updatePlayImgFromVideoThreadSlot(QImage )));
    connect(&(this->videoDecodeThread),
            SIGNAL(updatePlayTimeToPlayerSignal(quint64)),
            this,
            SLOT(updatePlayTimeFromVideoThreadSlot(quint64 )));

}

void videoPlayer::disconnectSignalAndSlotToAudioVideoThread()
{
    disconnect(&(this->videoDecodeThread),
            SIGNAL(updatePlayImgToPlayerSignal(QImage)),
            this,
            SLOT(updatePlayImgFromVideoThreadSlot(QImage )));
}


///////////////////////// 上层操作函数 /////////////////////////

void videoPlayer::playStart(QString videoName)
{
    emit startVideoPlaySignal(videoName);
}
void videoPlayer::playStop()
{
    emit stopVideoPlaySignal();
}
void videoPlayer::playPause()
{
    emit pauseVideoPlaySignal(true);
}
void videoPlayer::playContinue()
{
    emit pauseVideoPlaySignal(false);
}
void videoPlayer::playSeek(int value)
{
    emit seekVideoPlaySignal(value);
}
void videoPlayer::playChangeShowQImageSize(QSize size)
{
    this->UIPlay.imageDisplaySize = size;
}

/////////////////////// 线程控制 ///////////////////////////
int videoPlayer::threadStartUp()
{
    int ret;
    ret = decodingInit();
    if(ret == 0)
    {
        this->UIPlay.threadControl.runningStat = PLAY_RUNNING;
        qDebug()<<"videoPlayer::startUp()  :  启动音视频解码线程开始解码!";
        this->start();//解码器初始化成功启动线程
    }
    else
    {
        qWarning()<< tr("videoPlayer::startUp():  decodingInit() 初始化出错.");
    }
    return ret;
}

int videoPlayer::threadPause(bool pause)
{
    if(pause)
    {
        if(PLAY_RUNNING == this->UIPlay.threadControl.runningStat)
        {
            this->UIPlay.threadControl.runningStat = PLAY_PAUSE;
        }
        else if(PLAY_FINISHED == this->UIPlay.threadControl.runningStat)
        {
            this->playStart(this->videoName);
        }
    }
    else
    {
        if(PLAY_PAUSE == this->UIPlay.threadControl.runningStat)
        {
            this->UIPlay.threadControl.runningStat = PLAY_RUNNING;
        }
    }
    return 0;
}

int videoPlayer::threadQuit()
{
    this->UIPlay.threadControl.runningStat = PLAY_STOP;
    this->quit();
    this->wait();
    return 0;
}
int videoPlayer::threadSeek()
{
    this->UIPlay.threadControl.runningStat = PLAY_SEEK;
    return 0;
}


///////////////////////// 往上层传递一些消息 ////////////////////



///////////////////////// 槽函数定义 ///////////////////////////

void videoPlayer::startVideoPlaySlot(QString videoName)
{
    if(PLAY_STOP != this->UIPlay.threadControl.runningStat)
    {
        this->UIPlay.threadControl.runningStat = PLAY_STOP;
        while (this->isRunning()) //等待结束
        {
            msleep(10);
        }
    }

    this->setVideoName(videoName);
    this->threadStartUp();
}

void videoPlayer::threadStartSuccessSlot()
{

}

void videoPlayer::finishedVideoPlaySlot()
{

}

void videoPlayer::stopVideoPlaySlot()
{
    this->UIPlay.threadControl.runningStat = PLAY_STOP;
    if(this->isRunning())
    {
        this->quit();
        this->wait();
    }
}

void videoPlayer::pauseVideoPlaySlot(bool pause)
{
    threadPause(pause);
}

void videoPlayer::seekVideoPlaySlot(int value)
{
    if(PLAY_FINISHED == this->UIPlay.threadControl.runningStat)
    {
        this->UIPlay.threadControl.videoFileEnd = false;
    }
    if(PLAY_STOP == this->UIPlay.threadControl.runningStat)
    {
        return;
    }
    if(PLAY_SEEK == this->UIPlay.threadControl.runningStat)
    {
        this->UIPlay.threadControl.runningStat = PLAY_PAUSE;
        msleep(10);
    }
    this->UIPlay.videoSeekTime = (quint64)((value * 1.0 / 100 ) * this->UIPlay.videoAllTime);
    this->UIPlay.threadControl.runningStat = PLAY_SEEK;
    qDebug() << tr("videoPlayer::seekVideoPlaySlot(int value):   进行跳转!, 跳转到 %1s ").arg(this->UIPlay.videoSeekTime);

}

void videoPlayer::updatePlayTimeFromVideoThreadSlot(quint64 playTime)
{
    this->UIPlay.videoPlayTime = playTime;
    this->sendPlayTime();
}

void videoPlayer::updatePlayImgFromVideoThreadSlot(QImage img)
{
    this->sendPlayImg(img);
}

int videoPlayer::setAudioCodecVideoCodec()
{
    for(int i = 0; i < this->Decoding.format_ctx->nb_streams; ++i)
    {
        const AVStream* stream = this->Decoding.format_ctx->streams[i];
        qDebug()<< (tr("videoPlayer::setAudioCodecVideoCodec():  原始数据流使用的编码器类型: %1\n").arg(stream->codecpar->codec_id));

        //判断是否是视频流
        if(stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            //查找解码器
            this->Decoding.videoCodec=avcodec_find_decoder(stream->codecpar->codec_id);
            //打开解码器
            int err = avcodec_open2(stream->codec,this->Decoding.videoCodec,nullptr);
            if(err !=0 )
            {
                  qDebug()<< (tr("videoPlayer::setAudioCodecVideoCodec():  视频流需要的解码器打开失败.错误码 : %1 \n").arg(err));
                  return err;
            }
            this->Decoding.videoStreamIndex = i;
            this->Decoding.videoStream = this->Decoding.format_ctx->streams[i]; //保存视频流信息
            qDebug() << (tr("videoPlayer::setAudioCodecVideoCodec():  视频帧的尺寸(以像素为单位): (宽X高)%1x%2 像素格式: %3\n")   \
                                                                                            .arg(stream->codecpar->width)   \
                                                                                            .arg(stream->codecpar->height)  \
                                                                                            .arg(stream->codecpar->format));
        }
        //判断是否是音频流
        else if(stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {

            //查找解码器
            this->Decoding.audioCodec = avcodec_find_decoder(stream->codecpar->codec_id);

            //打开解码器
            int err = avcodec_open2(stream->codec,this->Decoding.audioCodec, nullptr);
            if(err !=0 )
            {
                  qDebug() << (tr("videoPlayer::setAudioCodecVideoCodec():  音频流需要的解码器打开失败. 错误码: %1 \n").arg(err));
                  return err;
            }
            this->Decoding.audioStream = this->Decoding.format_ctx->streams[i];
            this->Decoding.audioStreamIndex = i;
        }
    }
    if(   this->Decoding.audioStreamIndex == -1
       && this->Decoding.videoStreamIndex == -1)
    {
        qWarning() << tr("videoPlayer::setAudioCodecVideoCodec :  没有找到流");
        return -1;
    }
    return 0;
}

int videoPlayer::decodingInit()
{
    int ret;
    int execStage = 0;//执行阶段
    ret = AVFormatContextInit();
    if(ret < 0)
    {
        goto ERR;
    }
    execStage++; //1
    if( !(this->videoName.isEmpty()))
    {
        ret = this->openVideoFile();//打开文件
        if(ret != 0)
        {
            goto ERR;
        }
    }
    else
    {
        qDebug()<< "videoPlayer::decodingInit():   请先选择视频文件";
        goto ERR;//文件名为空
    }
    execStage++;//2
    //获取流信息
    ret = this->getVideoAudioStreamInfo();
    if(ret < 0)
    {   
        goto ERR;
    }
    execStage++;//3
    //获取并打开解码器
    ret = this->setAudioCodecVideoCodec();
    if(ret != 0)
    {
        //Close an opened input AVFormatContext.        goto ERR;
    }
    execStage++;//4
    this->UIPlay.videoAllTime = this->Decoding.format_ctx->duration / AV_TIME_BASE;
    qDebug()<< tr("videoPlayer::decodingInit():  解码器初始化成功, 此视频总播放时间为: %1:%2:%3")         \
                                                             .arg(this->UIPlay.videoAllTime/3600)   \
                                                             .arg(this->UIPlay.videoAllTime/60)     \
                                                             .arg(this->UIPlay.videoAllTime%60) ;

    return  0;

ERR:
    qWarning()<<tr("videoPlayer::decodingInit():  初始化解码器失败, 请按照一下步骤重新操作: ");
    switch(execStage)
    {
    case 3:
        this->Decoding. videoStreamIndex = -1;
        this->Decoding. audioStreamIndex = -1;
        this->Decoding. audioStream = Q_NULLPTR;
        this->Decoding. videoStream = Q_NULLPTR;
    case 2:
        //Close an opened input AVFormatContext.
        avformat_close_input(&(this->Decoding.format_ctx));

    case 1:
        //释放 AVFormatContext
        avformat_free_context(this->Decoding. format_ctx);

    }
    qWarning()<<tr("videoPlayer::decodingInit():  初始化失败, 释放参数完成!");
    return ret;
}
void videoPlayer::decodingDelete()
{
    //Close an opened input AVFormatContext.
    avformat_close_input(&(this->Decoding.format_ctx));
    //关闭解码器
    avcodec_close(this->Decoding.audioStream->codec);
    avcodec_close(this->Decoding.videoStream->codec);
    this->Decoding. audioCodec = Q_NULLPTR;
    this->Decoding. videoCodec = Q_NULLPTR;
    this->Decoding. audioStream = Q_NULLPTR;
    this->Decoding. videoStream = Q_NULLPTR;
    this->Decoding. videoAudioAVPacketQueue.clear_all_audio();
    this->Decoding. videoAudioAVPacketQueue.clear_all_video();
    this->Decoding. videoStreamIndex = -1;
    this->Decoding. audioStreamIndex = -1;
    //释放 AVFormatContext
    avformat_free_context(this->Decoding. format_ctx);
    this->UIPlay.videoAllTime = 0;
}

//跳转到指定帧
int videoPlayer::seekToDesignatedFrame(AVFormatContext* format_ctx, int StreamIndex, quint64 SeekTime, AVPacket* keyframe_pkt)
{
    int64_t seek_target = SeekTime*AV_TIME_BASE;
    int64_t seek_pos;
    int64_t cur_pos;
    double cur_clock;
    AVPacket pkt;
    AVRational aVRational = {1, AV_TIME_BASE};
    if(StreamIndex >= 0)
    {
        seek_pos = av_rescale_q(seek_target, aVRational,
                format_ctx->streams[StreamIndex]->time_base);
    }
    int seek_ret = av_seek_frame( format_ctx,
                                  StreamIndex,
                                  seek_pos,
                                  AVSEEK_FLAG_BACKWARD);

    if(seek_ret < 0)
    {
      qDebug() << "videoPlayer::seekToDesignatedFrame(AVFormatContext* format_ctx, int StreamIndex, quint64 SeekTime):    执行 av_seek_frame() 跳转失败!";
      return seek_ret;
    }

    seek_ret = av_read_frame(format_ctx, &pkt);
    if(seek_ret < 0)
    {
        qDebug() << "videoPlayer::seekToDesignatedFrame(AVFormatContext* format_ctx, int StreamIndex, quint64 SeekTime):    执行 av_read_frame(format_ctx, &pkt) 失败!";
        return seek_ret;
    }
    *keyframe_pkt = pkt;//保存关键帧
    cur_clock = pkt.pts*av_q2d(format_ctx->streams[StreamIndex]->time_base);
    cur_pos = cur_clock * AV_TIME_BASE;
    qDebug() << tr("seek_target: %1, seek_pos: %2, cur_pos= %3, cur_clock=%4").arg(seek_target).arg(seek_pos).arg(cur_pos).arg(cur_clock);
    if(cur_pos < seek_target)
    {
        while(1)
        {

            seek_ret = av_read_frame(format_ctx, &pkt);
            if(seek_ret < 0)
            {
                qDebug() << "videoPlayer::seekToDesignatedFrame(AVFormatContext* format_ctx, int StreamIndex, quint64 SeekTime):    执行 av_read_frame(format_ctx, &pkt) 失败!";
                return seek_ret;
            }
            if(pkt.stream_index == StreamIndex)
            {
                cur_clock = pkt.pts * av_q2d(format_ctx->streams[StreamIndex]->time_base);
                cur_pos = cur_clock * AV_TIME_BASE;
                if(cur_pos >= seek_target)
                {
                    av_packet_unref(&pkt);
                    break;
                }
            }
            else
            {
                av_packet_unref(&pkt);
                continue;
            }

            //qDebug()<< "哈哈哈哈哈" ;
        }
    }
    return 0;
}

void videoPlayer::run()
{
    int ret ;
    //开始循环读取流数据
    AVPacket pkt;
    this->audioDecodeThread.threadStartUp();
    this->videoDecodeThread.threadStartUp();
    this->sendThreadStartSuccess();
    while(1)
    {
        switch(this->UIPlay.threadControl.runningStat)
        {
        case PLAY_STOP:
        {
            this->UIPlay.threadSync.playerThreadStat = PLAY_STOP;
            //清除队列里面的数据
            this->Decoding.videoAudioAVPacketQueue.clear_all_audio();
            this->Decoding.videoAudioAVPacketQueue.clear_all_video();
            qDebug()<<tr("videoPlayer::run():  stat == PLAY_STOP , videoPlayer 线程退出");
            goto END;
        }
            break;
        case PLAY_RUNNING:
        {
            this->UIPlay.threadSync.playerThreadStat = PLAY_RUNNING;
            //判断队列里的数据是否满了，满了就暂时不读取等待一段时间再读取
            if(   this->Decoding.videoAudioAVPacketQueue.get_audio_pack_cnt() >= QUEUE_DATA_CNT
               || this->Decoding.videoAudioAVPacketQueue.get_video_pack_cnt() >= QUEUE_DATA_CNT
                )
            {
                qDebug() << tr("videoPlayer::run():   队列里的数据满了, 等待解码!");
                msleep(100);
                continue;
            }
            //读取一帧数据
            ret=av_read_frame(this->Decoding.format_ctx, &pkt);
            if(ret < 0 )
            {
                qDebug()<<tr("videoPlayer::run()  :  数据读取完毕!");
                if(   this->Decoding.videoAudioAVPacketQueue.get_audio_pack_cnt() == 0
                   && this->Decoding.videoAudioAVPacketQueue.get_video_pack_cnt() == 0
                      )
                {
                    qDebug()<<tr("videoPlayer::run()  :  视频播放完成, videoPlayer 线程切换为PLAY_FINISHED 状态!");
                    this->UIPlay.threadControl.videoFileEnd = true;
                    this->sendFinishedVideoPlay();
                    this->UIPlay.threadControl.runningStat = PLAY_FINISHED; //设置状态为播放完成
                }
                //msleep(10);
            }
            else // 将视频音频数据加入相应的队列
            {
                if(pkt.stream_index == this->Decoding.audioStreamIndex)
                {
                    //qDebug()<<tr("videoPlayer::run()  :  加入Audio队列!");
                    this->Decoding.videoAudioAVPacketQueue.write_audio_pack(pkt);
                }
                else if(pkt.stream_index == this->Decoding.videoStreamIndex)
                {
                    //qDebug()<<tr("videoPlayer::run()  :  加入Video队列!");
                    this->Decoding.videoAudioAVPacketQueue.write_video_pack(pkt);
                }
                else
                {
                    av_packet_unref(&pkt);
                }
            }

        }
            break;
        case PLAY_PAUSE:
        {
            this->UIPlay.threadSync.playerThreadStat = PLAY_PAUSE;
            msleep(100);
        }
            break;
        case PLAY_SEEK:
        {
            this->UIPlay.threadSync.playerThreadStat = PLAY_SEEK;
            this->Decoding.videoAudioAVPacketQueue.clear_all_audio(); //清除音频数据
            this->Decoding.videoAudioAVPacketQueue.clear_all_video(); //清除视频数据

            int64_t seek_target = this->UIPlay.videoSeekTime*AV_TIME_BASE;
            int64_t seek_really ;
            AVRational aVRational = {1, AV_TIME_BASE};
            if(this->Decoding.videoStreamIndex >= 0)
            {
                seek_really = av_rescale_q(seek_target, aVRational,
                        this->Decoding.format_ctx->streams[this->Decoding.videoStreamIndex]->time_base);
            }
            int seek_ret = av_seek_frame( this->Decoding.format_ctx,
                                          this->Decoding.videoStreamIndex,
                                          seek_really,
                                          AVSEEK_FLAG_BACKWARD);
            if(seek_ret < 0)
            {
                this->UIPlay.threadControl.runningStat = PLAY_STOP;
            }
            qDebug() << tr("seek_target: %1, seek_really: %2").arg(seek_target).arg(seek_really);
            
            /*else
            {
                this->UIPlay.threadControl.runningStat = PLAY_RUNNING;
                qDebug()<<"跳转成功:"<<seek_target<<",状态:"<< seek_ret;
            }*/


            /*AVPacket pkt;
            ret = seekToDesignatedFrame(this->Decoding.format_ctx, this->Decoding.videoStreamIndex, this->UIPlay.videoSeekTime, &pkt);
            if(ret < 0)
            {
                this->UIPlay.threadControl.runningStat = PLAY_STOP;
            }
            else
            {
                this->Decoding.videoAudioAVPacketQueue.write_video_pack(pkt);
                this->UIPlay.threadControl.runningStat = PLAY_RUNNING;
                //qDebug()<<"跳转成功:"<<seek_target<<",状态:"<< seek_ret;
            }*/
            while(PLAY_SEEK == this->UIPlay.threadControl.runningStat)
            {
                msleep(10);
            }
            qDebug()<<"跳转成功:"<<seek_target<<",状态:"<< seek_ret;
        }
            break;
        case PLAY_FINISHED:
        {
            this->UIPlay.threadSync.playerThreadStat = PLAY_FINISHED;
            msleep(100);
            this->sendFinishedVideoPlay();
        }
            break;
        }// switch(this->UIPlay.threadControl.runningStat)
    }

END:
    qDebug() << tr("videoPlayer::run():   等待解码线程退出!");

    //发送视频播放完成信号
    //解码结束
    if(this->audioDecodeThread.isRunning())
    {
        this->audioDecodeThread.quit();
        this->audioDecodeThread.wait();
    }

    if(this->videoDecodeThread.isRunning())
    {
        this->videoDecodeThread.quit();
        this->videoDecodeThread.wait();
    }
    this->sendFinishedVideoPlayThread(); //发送播放线程即将结束信号
    qDebug() << tr("videoPlayer::run():   解码结束,进行资源释放!");
    decodingDelete();//释放当前解码相关资源

}
